tests/test-libarchive-import.c: add tests
authorJonathan Lebon <jlebon@redhat.com>
Fri, 22 Apr 2016 16:42:05 +0000 (12:42 -0400)
committerColin Walters (automation) <walters+githubbot@verbum.org>
Fri, 6 May 2016 14:44:55 +0000 (14:44 +0000)
- Do a bit of refactoring
- Add test for use_ostree_convention
- Add test for xattr_callback
- Add test for SELinux labeling

Closes: #275
Approved by: cgwalters

tests/test-libarchive-import.c

index 877fa77cf9e868bbefba6c8f38b3056c5b5add29..aaa3c37b2ce650d3fc5b766e1436949e38165a78 100644 (file)
@@ -42,6 +42,8 @@ test_data_init (TestData *td)
   GError *error = NULL;
   struct archive *a = archive_write_new ();
   struct archive_entry *ae;
+  uid_t uid = getuid ();
+  gid_t gid = getgid ();
 
   td->tmpd = g_mkdtemp (g_strdup ("/var/tmp/test-libarchive-import-XXXXXX"));
   g_assert_cmpint (0, ==, chdir (td->tmpd));
@@ -59,12 +61,16 @@ test_data_init (TestData *td)
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/");
   archive_entry_set_mode (ae, S_IFDIR | 0755);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
   archive_entry_free (ae);
 
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/file");
   archive_entry_set_mode (ae, S_IFREG | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   archive_entry_set_size (ae, 4);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
   g_assert_cmpint (4, ==, archive_write_data (a, "foo\n", 4));
@@ -73,6 +79,8 @@ test_data_init (TestData *td)
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/devnull");
   archive_entry_set_mode (ae, S_IFCHR | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   archive_entry_set_devmajor (ae, 1);
   archive_entry_set_devminor (ae, 3);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
@@ -81,6 +89,26 @@ test_data_init (TestData *td)
   ae = archive_entry_new ();
   archive_entry_set_pathname (ae, "/anotherfile");
   archive_entry_set_mode (ae, S_IFREG | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
+  archive_entry_set_size (ae, 4);
+  g_assert_cmpint (0, ==, archive_write_header (a, ae));
+  g_assert_cmpint (4, ==, archive_write_data (a, "bar\n", 4));
+  archive_entry_free (ae);
+
+  ae = archive_entry_new ();
+  archive_entry_set_pathname (ae, "/etc");
+  archive_entry_set_mode (ae, S_IFDIR | 0755);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
+  g_assert_cmpint (0, ==, archive_write_header (a, ae));
+  archive_entry_free (ae);
+
+  ae = archive_entry_new ();
+  archive_entry_set_pathname (ae, "/etc/file");
+  archive_entry_set_mode (ae, S_IFREG | 0777);
+  archive_entry_set_uid (ae, uid);
+  archive_entry_set_gid (ae, gid);
   archive_entry_set_size (ae, 4);
   g_assert_cmpint (0, ==, archive_write_header (a, ae));
   g_assert_cmpint (4, ==, archive_write_data (a, "bar\n", 4));
@@ -123,6 +151,15 @@ spawn_cmdline (const char *cmd, GError **error)
   return TRUE;
 }
 
+static void
+test_archive_setup (int fd, struct archive *a)
+{
+  g_assert_cmpint (0, ==, lseek (fd, 0, SEEK_SET));
+  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
+  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
+  g_assert_cmpint (0, ==, archive_read_open_fd (a, fd, 8192));
+}
+
 static void
 test_libarchive_noautocreate_empty (gconstpointer data)
 {
@@ -132,10 +169,7 @@ test_libarchive_noautocreate_empty (gconstpointer data)
   OstreeRepoImportArchiveOptions opts = { 0, };
   glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
 
-  g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192));
+  test_archive_setup (td->fd_empty, a);
 
   (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
   g_assert_no_error (error);
@@ -153,10 +187,7 @@ test_libarchive_autocreate_empty (gconstpointer data)
 
   opts.autocreate_parents = 1;
 
-  g_assert_cmpint (0, ==, lseek (td->fd_empty, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd_empty, 8192));
+  test_archive_setup (td->fd_empty, a);
 
   (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
   g_assert_no_error (error);
@@ -172,68 +203,95 @@ test_libarchive_error_device_file (gconstpointer data)
   OstreeRepoImportArchiveOptions opts = { 0, };
   glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
 
-  g_assert_cmpint (0, ==, lseek (td->fd, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd, 8192));
+  test_archive_setup (td->fd, a);
 
   (void)ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error);
   g_assert (error != NULL);
 }
 
-static void
-test_libarchive_ignore_device_file (gconstpointer data)
+static gboolean
+skip_if_no_xattr (TestData *td)
 {
-  TestData *td = (void*)data;
-  GError *error = NULL;
-  GCancellable *cancellable = NULL;
-  struct archive *a = archive_read_new ();
-  OstreeRepoImportArchiveOptions opts = { 0, };
-  glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
-  glnx_unref_object GFile *root = NULL;
-  g_autofree char *commit_checksum = NULL;
-
+  /* /var/tmp might actually be a tmpfs */
   if (setxattr (td->tmpd, "user.test-xattr-support", "yes", 4, 0) != 0)
     {
       int saved_errno = errno;
-      g_autofree gchar *message = g_strdup_printf ("unable to setxattr on \"%s\": %s", td->tmpd, g_strerror (saved_errno));
-
+      g_autofree gchar *message
+        = g_strdup_printf ("unable to setxattr on \"%s\": %s",
+                           td->tmpd, g_strerror (saved_errno));
       g_test_skip (message);
-      goto out;
+      return TRUE;
     }
 
-  g_assert_cmpint (0, ==, lseek (td->fd, 0, SEEK_SET));
-  g_assert_cmpint (0, ==, archive_read_support_format_all (a));
-  g_assert_cmpint (0, ==, archive_read_support_filter_all (a));
-  g_assert_cmpint (0, ==, archive_read_open_fd (a, td->fd, 8192));
+  return FALSE;
+}
 
-  opts.ignore_unsupported_content = TRUE;
+static gboolean
+import_write_and_ref (OstreeRepo      *repo,
+                      OstreeRepoImportArchiveOptions *opts,
+                      struct archive  *a,
+                      const char      *ref,
+                      OstreeRepoCommitModifier *modifier,
+                      GError         **error)
+{
+  gboolean ret = FALSE;
+  glnx_unref_object GFile *root = NULL;
+  g_autofree char *commit_checksum = NULL;
+  glnx_unref_object OstreeMutableTree *mtree = ostree_mutable_tree_new ();
 
-  if (!ostree_repo_prepare_transaction (td->repo, NULL, cancellable, &error))
+  if (!ostree_repo_prepare_transaction (repo, NULL, NULL, error))
     goto out;
 
-  if (!ostree_repo_import_archive_to_mtree (td->repo, &opts, a, mtree, NULL, NULL, &error))
+  if (!ostree_repo_import_archive_to_mtree (repo, opts, a, mtree, modifier,
+                                            NULL, error))
     goto out;
 
-  if (!ostree_repo_write_mtree (td->repo, mtree, &root, cancellable, &error))
+  if (!ostree_repo_write_mtree (repo, mtree, &root, NULL, error))
     goto out;
 
-  if (!ostree_repo_write_commit (td->repo, NULL, "", "", NULL,
+  if (!ostree_repo_write_commit (repo, NULL, "", "", NULL,
                                  OSTREE_REPO_FILE (root),
-                                 &commit_checksum, cancellable, &error))
+                                 &commit_checksum, NULL, error))
     goto out;
 
-  ostree_repo_transaction_set_ref (td->repo, NULL, "foo", commit_checksum);
-  
-  if (!ostree_repo_commit_transaction (td->repo, NULL, cancellable, &error))
+  ostree_repo_transaction_set_ref (repo, NULL, ref, commit_checksum);
+
+  if (!ostree_repo_commit_transaction (repo, NULL, NULL, error))
+    goto out;
+
+  ret = TRUE;
+out:
+  return ret;
+}
+
+static void
+test_libarchive_ignore_device_file (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0, };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  test_archive_setup (td->fd, a);
+
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "foo", NULL, &error))
     goto out;
 
+  /* check contents */
   if (!spawn_cmdline ("ostree --repo=repo ls foo file", &error))
     goto out;
 
   if (!spawn_cmdline ("ostree --repo=repo ls foo anotherfile", &error))
     goto out;
 
+  if (!spawn_cmdline ("ostree --repo=repo ls foo /etc/file", &error))
+    goto out;
+
   if (spawn_cmdline ("ostree --repo=repo ls foo devnull", &error))
     g_assert_not_reached ();
   g_assert (error != NULL);
@@ -243,6 +301,243 @@ test_libarchive_ignore_device_file (gconstpointer data)
   g_assert_no_error (error);
 }
 
+static gboolean
+check_ostree_convention (GError *error)
+{
+  if (!spawn_cmdline ("ostree --repo=repo ls bar file", &error))
+    return FALSE;
+
+  if (!spawn_cmdline ("ostree --repo=repo ls bar anotherfile", &error))
+    return FALSE;
+
+  if (!spawn_cmdline ("ostree --repo=repo ls bar /usr/etc/file", &error))
+    return FALSE;
+
+  if (spawn_cmdline ("ostree --repo=repo ls bar /etc/file", &error))
+    g_assert_not_reached ();
+  g_assert (error != NULL);
+  g_clear_error (&error);
+
+  if (spawn_cmdline ("ostree --repo=repo ls bar devnull", &error))
+    g_assert_not_reached ();
+  g_assert (error != NULL);
+  g_clear_error (&error);
+
+  return TRUE;
+}
+
+static void
+test_libarchive_ostree_convention (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0, };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  test_archive_setup (td->fd, a);
+
+  opts.autocreate_parents = TRUE;
+  opts.use_ostree_convention = TRUE;
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "bar", NULL, &error))
+    goto out;
+
+  if (!check_ostree_convention (error))
+    goto out;
+
+ out:
+  g_assert_no_error (error);
+}
+
+static GVariant*
+xattr_cb (OstreeRepo  *repo,
+          const char  *path,
+          GFileInfo   *file_info,
+          gpointer     user_data)
+{
+  g_auto(GVariantBuilder) builder;
+  g_variant_builder_init (&builder, (GVariantType*)"a(ayay)");
+  if (strcmp (path, "/anotherfile") == 0)
+    g_variant_builder_add (&builder, "(@ay@ay)",
+                           g_variant_new_bytestring ("user.data"),
+                           g_variant_new_bytestring ("mydata"));
+  return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+static void
+test_libarchive_xattr_callback (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0 };
+  OstreeRepoCommitModifier *modifier = NULL;
+  char buf[7] = { 0 };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+  ostree_repo_commit_modifier_set_xattr_callback (modifier, xattr_cb,
+                                                  NULL, NULL);
+
+  test_archive_setup (td->fd, a);
+
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "baz", modifier, &error))
+    goto out;
+
+  /* check contents */
+  if (!spawn_cmdline ("ostree --repo=repo checkout baz baz-checkout", &error))
+    goto out;
+
+  g_assert_cmpint (0, >, getxattr ("baz-checkout/file", "user.data", NULL, 0));
+  g_assert_cmpint (ENODATA, ==, errno);
+
+  if (getxattr ("baz-checkout/anotherfile", "user.data", buf, sizeof buf) < 0)
+    {
+      glnx_set_prefix_error_from_errno (&error, "%s", "getxattr");
+      goto out;
+    }
+
+  g_assert_cmpstr (buf, ==, "mydata");
+
+ out:
+  if (modifier)
+    ostree_repo_commit_modifier_unref (modifier);
+  g_assert_no_error (error);
+}
+
+static GVariant*
+path_cb (OstreeRepo  *repo,
+         const char  *path,
+         GFileInfo   *file_info,
+         gpointer     user_data)
+{
+  if (strcmp (path, "/etc/file") == 0)
+    *(gboolean*)user_data = TRUE;
+  return NULL;
+}
+
+static void
+entry_pathname_test_helper (gconstpointer data, gboolean on)
+{
+  TestData *td = (void*)data; GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0, };
+  OstreeRepoCommitModifier *modifier = NULL;
+  gboolean met_etc_file = FALSE;
+
+  modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+  ostree_repo_commit_modifier_set_xattr_callback (modifier, path_cb,
+                                                  NULL, &met_etc_file);
+
+  test_archive_setup (td->fd, a);
+
+  opts.autocreate_parents = TRUE;
+  opts.use_ostree_convention = TRUE;
+  opts.ignore_unsupported_content = TRUE;
+  opts.callback_with_entry_pathname = on;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "bar", modifier, &error))
+    goto out;
+
+  /* the flag shouldn't have any effect on the final tree */
+  if (!check_ostree_convention (error))
+    goto out;
+
+  if (!on && met_etc_file)
+    {
+      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Received callback with /etc/file");
+      goto out;
+    }
+
+  if (on && !met_etc_file)
+    {
+      g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Did not receive callback with /etc/file");
+      goto out;
+    }
+
+ out:
+  g_assert_no_error (error);
+}
+
+static void
+test_libarchive_no_use_entry_pathname (gconstpointer data)
+{
+  entry_pathname_test_helper (data, FALSE);
+}
+
+static void
+test_libarchive_use_entry_pathname (gconstpointer data)
+{
+  entry_pathname_test_helper (data, TRUE);
+}
+
+static void
+test_libarchive_selinux (gconstpointer data)
+{
+  TestData *td = (void*)data;
+  GError *error = NULL;
+  struct archive *a = archive_read_new ();
+  OstreeRepoImportArchiveOptions opts = { 0 };
+  glnx_unref_object OstreeSePolicy *sepol = NULL;
+  OstreeRepoCommitModifier *modifier = NULL;
+  char buf[64] = { 0 };
+
+  if (skip_if_no_xattr (td))
+    goto out;
+
+  {
+    glnx_unref_object GFile *root = g_file_new_for_path ("/");
+
+    /* creation should always succeed */
+    sepol = ostree_sepolicy_new (root, NULL, &error);
+    g_assert (sepol != NULL);
+  }
+
+  if (ostree_sepolicy_get_name (sepol) == NULL)
+    {
+      g_test_skip ("SELinux disabled");
+      goto out;
+    }
+
+  modifier = ostree_repo_commit_modifier_new (0, NULL, NULL, NULL);
+  ostree_repo_commit_modifier_set_sepolicy (modifier, sepol);
+
+  test_archive_setup (td->fd, a);
+
+  opts.ignore_unsupported_content = TRUE;
+
+  if (!import_write_and_ref (td->repo, &opts, a, "bob", modifier, &error))
+    goto out;
+
+  /* check contents */
+  if (!spawn_cmdline ("ostree --repo=repo checkout bob bob-checkout", &error))
+    goto out;
+
+  if (getxattr ("bob-checkout/etc", "security.selinux", buf, sizeof buf) < 0)
+    {
+      glnx_set_prefix_error_from_errno (&error, "%s", "getxattr");
+      goto out;
+    }
+
+  buf[(sizeof buf) - 1] = '\0';
+  g_assert_cmpstr (buf, ==, "system_u:object_r:etc_t:s0");
+
+ out:
+  if (modifier)
+    ostree_repo_commit_modifier_unref (modifier);
+  g_assert_no_error (error);
+}
+
 int main (int argc, char **argv)
 {
   TestData td = {NULL,};
@@ -256,10 +551,15 @@ int main (int argc, char **argv)
   g_test_add_data_func ("/libarchive/autocreate-empty", &td, test_libarchive_autocreate_empty);
   g_test_add_data_func ("/libarchive/error-device-file", &td, test_libarchive_error_device_file);
   g_test_add_data_func ("/libarchive/ignore-device-file", &td, test_libarchive_ignore_device_file);
+  g_test_add_data_func ("/libarchive/ostree-convention", &td, test_libarchive_ostree_convention);
+  g_test_add_data_func ("/libarchive/xattr-callback", &td, test_libarchive_xattr_callback);
+  g_test_add_data_func ("/libarchive/no-use-entry-pathname", &td, test_libarchive_no_use_entry_pathname);
+  g_test_add_data_func ("/libarchive/use-entry-pathname", &td, test_libarchive_use_entry_pathname);
+  g_test_add_data_func ("/libarchive/selinux", &td, test_libarchive_selinux);
 
   r = g_test_run();
 
-  if (td.tmpd)
+  if (td.tmpd && g_getenv ("TEST_SKIP_CLEANUP") == NULL)
     (void) glnx_shutil_rm_rf_at (AT_FDCWD, td.tmpd, NULL, NULL);
   return r;
 }